<?php
namespace app\service;

use OSS\OssClient;
use OSS\Core\OssException;
use think\facade\Config;
use think\facade\Log;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Exception\ClientException; // 新增导入

class OssService
{
    // 允许的文件类型
    const ALLOWED_EXTENSIONS = [
        'image' => ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg'],
        'video' => ['mp4', 'mov', 'avi', 'wmv', 'flv', 'm3u8', 'ts', 'mkv', 'webm', 'm4v']
    ];

    // 最大文件大小（100MB）
    const MAX_FILE_SIZE = 100 * 1024 * 1024;

    /**
     * 获取OSS配置
     */
    private static function getOssConfig(): array
    {
        return Config::get('oss', []);
    }

    /**
     * 获取OSS客户端实例
     */
    private static function getOssClient(): OssClient
    {
        $config = self::getOssConfig();

        try {
            return new OssClient(
                $config['access_key_id'],
                $config['access_key_secret'],
                $config['endpoint'],
                $config['is_cname'] ?? false
            );
        } catch (OssException $e) {
            Log::error('OSS客户端初始化失败: ' . $e->getMessage());
            throw new \Exception('OSS服务初始化失败');
        }
    }

    /**
     * 获取完整的文件URL
     */
    public static function getFileUrl(string $fileName): string
    {
        $config = self::getOssConfig();

        // 使用自定义域名（如果有），否则使用默认域名
        if (!empty($config['custom_domain'])) {
            return 'https://' . rtrim($config['custom_domain'], '/') . '/' . ltrim($fileName, '/');
        }

        return 'https://' . $config['bucket'] . '.' . $config['endpoint'] . '/' . ltrim($fileName, '/');
    }

    /**
     * 批量通过远程URL上传文件到OSS
     */
    public static function batchUploadFromUrls(array $urls, string $fileType = 'image', string $subDir = null): array
    {
        $results = [
            'success' => [],
            'failed' => []
        ];

        // 使用默认目录如果未指定
        if ($subDir === null) {
            $config = self::getOssConfig();
            $subDir = $config['default_dir'] ?? 'uploads';
        }

        foreach ($urls as $index => $url) {
            if (empty($url)) {
                $results['failed'][] = [
                    'url' => $url,
                    'error' => 'URL为空'
                ];
                continue;
            }

            $result = self::uploadSingleFile($url, $fileType, $subDir, $index);

            if ($result['success']) {
                $results['success'][] = $result;
            } else {
                $results['failed'][] = $result;
            }
        }

        return $results;
    }

    /**
     * 上传单个文件
     */
    public static function uploadSingleFile(string $url, string $fileType, string $subDir, int $index): array
    {
        try {
            // 1. 获取文件信息
            $fileInfo = self::getRemoteFileInfo($url);

            // 2. 验证文件类型
            if (!self::isAllowedExtension($fileInfo['extension'], $fileType)) {
                throw new \Exception("不支持的文件类型: {$fileInfo['extension']}");
            }

            // 3. 获取文件内容
            $fileContent = self::getRemoteFileContent($url, $fileInfo['size']);

            // 4. 生成文件名
            $fileName = self::generateFileName($subDir, $fileType, $fileInfo['extension'], $index);

            // 5. 上传到OSS
            $ossClient = self::getOssClient();
            $config = self::getOssConfig();

            $ossClient->putObject($config['bucket'], $fileName, $fileContent);

            // 6. 获取访问URL
            $fileUrl = self::getFileUrl($fileName);

            return [
                'success' => true,
                'original_url' => $url,
                'oss_url' => $fileUrl,
                'file_name' => $fileName,
                'file_size' => $fileInfo['size'],
                'message' => '上传成功'
            ];

        } catch (GuzzleException $e) {
            $error = "网络请求失败: " . $e->getMessage();
            Log::error("OSS上传网络错误: {$e->getMessage()} URL: {$url}");
        } catch (OssException $e) {
            $error = "OSS上传错误: " . $e->getMessage();
            Log::error("OSS上传失败: {$e->getMessage()} URL: {$url}");
        } catch (\Exception $e) {
            $error = $e->getMessage();
            Log::error("OSS上传异常: {$e->getMessage()} URL: {$url}");
        }

        return [
            'success' => false,
            'original_url' => $url,
            'error' => $error ?? '未知错误',
            'oss_url' => '',
            'file_name' => ''
        ];
    }

    /**
     * 获取远程文件信息 - 主要修改点
     * 添加了对HEAD请求405错误的处理，自动降级为GET请求
     */
    private static function getRemoteFileInfo(string $url): array
    {
        $client = new Client([
            'timeout' => 10,
            'verify' => false,
            'headers' => [
                'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
            ]
        ]);

        // ========== 主要修改开始 ==========
        $response = null;
        $usedGetMethod = false;

        try {
            // 先尝试使用HEAD请求获取文件信息（更高效）
            $response = $client->head($url);
        } catch (ClientException $e) {
            // 如果是405错误（Method Not Allowed），改用GET请求
            if ($e->getCode() === 405) {
                try {
                    $response = $client->get($url);
                    $usedGetMethod = true;
                    Log::info("URL不支持HEAD方法，已自动降级为GET方法: {$url}");
                } catch (\Exception $getException) {
                    throw new \Exception("HEAD和GET方法均失败: " . $getException->getMessage());
                }
            } else {
                throw new \Exception("HEAD请求失败: " . $e->getMessage());
            }
        } catch (\Exception $e) {
            throw new \Exception("获取文件信息失败: " . $e->getMessage());
        }
        // ========== 主要修改结束 ==========

        $contentLength = $response->getHeaderLine('content-length');
        $contentType = $response->getHeaderLine('content-type');

        // 检查文件大小
        $fileSize = (int)$contentLength;
        if ($fileSize > self::MAX_FILE_SIZE) {
            throw new \Exception("文件大小超过限制 (" . self::MAX_FILE_SIZE / 1024 / 1024 . "MB)");
        }

        if ($fileSize === 0) {
            throw new \Exception("获取文件大小失败");
        }

        // 获取文件扩展名
        $originalName = pathinfo(parse_url($url, PHP_URL_PATH), PATHINFO_BASENAME);
        $originalName = explode('?', $originalName)[0];
        $extension = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));

        // 如果没有扩展名，尝试从Content-Type推断
        if (empty($extension)) {
            $extension = self::getExtensionFromContentType($contentType);
        }

        if (empty($extension)) {
            throw new \Exception("无法识别文件类型");
        }

        return [
            'size' => $fileSize,
            'extension' => $extension,
            'content_type' => $contentType,
            'used_get_method' => $usedGetMethod // 新增字段，用于调试
        ];
    }

    /**
     * 获取远程文件内容
     */
    private static function getRemoteFileContent(string $url, int $expectedSize): string
    {
        $client = new Client([
            'timeout' => 30,
            'verify' => false,
            'headers' => [
                'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
            ]
        ]);

        $response = $client->get($url);
        $content = $response->getBody()->getContents();

        // 验证文件大小
        if (strlen($content) !== $expectedSize) {
            throw new \Exception("文件下载不完整，期望: {$expectedSize}字节，实际: " . strlen($content) . "字节");
        }

        return $content;
    }

    /**
     * 生成文件名
     */
    private static function generateFileName(string $subDir, string $fileType, string $extension, int $index): string
    {
        $dateDir = date('Ymd');
        $uniqueId = uniqid();
        $randomStr = substr(md5(microtime() . $index), 0, 8);

        return trim("{$subDir}/{$fileType}/{$dateDir}/{$uniqueId}_{$randomStr}.{$extension}", '/');
    }

    /**
     * 检查是否允许的文件扩展名
     */
    private static function isAllowedExtension(string $extension, string $fileType): bool
    {
        return in_array($extension, self::ALLOWED_EXTENSIONS[$fileType] ?? []);
    }

    /**
     * 从Content-Type获取文件扩展名
     */
    private static function getExtensionFromContentType(string $contentType): string
    {
        $mappings = [
            'image/jpeg' => 'jpg',
            'image/png' => 'png',
            'image/gif' => 'gif',
            'image/webp' => 'webp',
            'image/svg+xml' => 'svg',
            'video/mp4' => 'mp4',
            'video/quicktime' => 'mov',
            'video/x-msvideo' => 'avi',
            'video/x-ms-wmv' => 'wmv',
            'video/x-flv' => 'flv',
            'application/x-mpegURL' => 'm3u8',
            'video/MP2T' => 'ts',
            'video/x-matroska' => 'mkv',
            'video/webm' => 'webm',
        ];

        // 提取主要的content-type（去除字符集等信息）
        $mainType = explode(';', $contentType)[0];

        return $mappings[$mainType] ?? '';
    }

    /**
     * 获取支持的文件类型信息
     */
    public static function getSupportedTypes(): array
    {
        return [
            'image' => [
                'extensions' => self::ALLOWED_EXTENSIONS['image'],
                'max_size' => self::MAX_FILE_SIZE,
                'max_size_mb' => round(self::MAX_FILE_SIZE / 1024 / 1024, 1)
            ],
            'video' => [
                'extensions' => self::ALLOWED_EXTENSIONS['video'],
                'max_size' => self::MAX_FILE_SIZE,
                'max_size_mb' => round(self::MAX_FILE_SIZE / 1024 / 1024, 1)
            ]
        ];
    }

    /**
     * 删除OSS文件
     */
    public static function deleteFile(string $fileName): bool
    {
        try {
            $ossClient = self::getOssClient();
            $config = self::getOssConfig();
            $ossClient->deleteObject($config['bucket'], $fileName);
            return true;
        } catch (OssException $e) {
            Log::error("OSS删除文件失败: {$e->getMessage()} File: {$fileName}");
            return false;
        }
    }

    /**
     * 检查OSS配置是否完整
     */
    public static function checkConfig(): array
    {
        $config = self::getOssConfig();
        $missing = [];

        if (empty($config['access_key_id'])) $missing[] = 'access_key_id';
        if (empty($config['access_key_secret'])) $missing[] = 'access_key_secret';
        if (empty($config['bucket'])) $missing[] = 'bucket';
        if (empty($config['endpoint'])) $missing[] = 'endpoint';

        return [
            'valid' => empty($missing),
            'missing' => $missing,
            'config' => array_diff_key($config, array_flip(['access_key_secret'])) // 不返回密钥
        ];
    }
}